

%module gimi

//for std::string
%include stl.i
%include std_string.i

%include "gimiprotocoltypes.i"
%include "gimiutils.i"
%{
#include "gimiutils.h"
#include "gimicore.h"
#include "gimi.h"


%}
/*
#include "gimimessage.h"
#include "multibuffer.h"
#include "gimimessagefilter.h"
#include "gimisubscribers.h"
#include "gimireplyhandler.h"
#include "gimisubscriptions.h"
#include "connector.h"
*/

/** GIMI Interface.
 * 
 * This namespace contains all the methods and types related to the
 * GIMI interface
 */
namespace gimi {

/**
 * \class GIMI
 *
 */
class GIMI {
  public:
    /**
     * Creates a new GIMI-object.
     *
     * \param[in] defaultBufferSize Sets a default maximum-size for all
     * messagequeues. When set to 0, GIMI won't receive data-messages.
     * \param[in] clientDescription Description of this GIMI-client.
     * \param[in] clientLocation Location of this GIMI-client.
     * \param[in] clientVersion Version of this GIMI-client. Format is linux-
     * standard (major.minor.extra) and is given in format:
     * 10000*majorversion + 100*minorversion + extraversion
     * \param[in] clientBuild Build number of this GIMI-client.
     */
    GIMI(int defaultBufferSize = GIMI_DEFAULTQUEUESIZE,
         const std::string clientDescription = "",
         const std::string clientLocation = "",
         const int clientVersion = 0,
         const int clientBuild = 0);

    /**
     * Cleans up this GIMI-object. Also handles disconnecting from hub.
     */
    ~GIMI();

    //// Reading settings /////////////////////////////////////////////////////
    
    /**
     * Read settings for GIMI from specified file.
     * Settings that can be adjusted are:
     * default queue size,
     * description,
     * location,
     * queue sizes for specific major+minor-types,
     * provided services,
     * accepted services,
     * hub address and port,
     * own hubname.
     *
     * Redirections can be added to settings if there is need.
     * 
     * See gimisettingsexample.xml for information about the format.
     * 
     * \param[in] filename Name of the settingsfile.
     * \return Boolean for success.
     */
    bool readSettings(const std::string &filename);
    
    /**
     * Query settings from an already constructed settings-class.
     * 
     * \param[in] settings Class containing the settings.
     * \return Boolean for success.
     */
    inline bool readSettings(const gim::CSettings &settings)
    { return gimiCore.readSettings(settings); };
    
    //// Queue adjusting //////////////////////////////////////////////////////
    
    /**
     * Set a new maximum size for given messagetype. This means that only that
     * number of messages are admitted to the queue. After filling older
     * messages are dropped. (FIFO)
     * 
     * New size must be > 0.
     *
     * \param[in] majorTypeId 
     * \param[in] minorTypeId
     * \param[in] mSize
     * \param[in] bufferId
     * \return True for success, false if given values were invalid.
     */
    inline bool setQueueMaxSize(const int majorTypeId,
                                const int minorTypeId,
                                const int mSize,
                                const unsigned int bufferId = 0)
    { return gimiCore.messageBuffers.setQueueMaxSize(majorTypeId, minorTypeId,
        mSize, bufferId); };
    
    /**
     * Returns current size of given queue. If majortype is left out, returns
     * size of all queues in GIMI's messagebuffer.
     * 
     * \param[in] majorTypeId
     * \param[in] minorTypeId
     * \param[in] bufferId
     * \return Size of given queue. (non-existing queues have size 0)
     */
    int getQueueSize(const int majorTypeId = GIMI_IGNOREMAJORTYPE,
                     const int minorTypeId = GIMI_IGNOREMINORTYPE,
                     const unsigned int bufferId = 0);
    
    //// Providing services ///////////////////////////////////////////////////
    
    /**
     * Adds a servicetype this client provides.
     * 
     * \param[in] majorTypeId Major-type of the service.
     * \param[in] minorTypeId Minor-type of the service.
     * \param[in] serviceDescription Optional description for the service.
     * \param[in] serviceLocation Optional Location-information of the service.
     * If empty, hostname is used.
     * \param[in] keepAliveInterval If your service does not send data with
     * regular intervals, you can define an automatical keepalive (interval
     * in milliseconds) for subscribers who use auto-resubscribing.
     * \return False if service-type already existed, else true.
     */
    inline bool addProvidedService(const int majorTypeId,
                                   const int minorTypeId,
                                   const std::string serviceDescription = "",
                                   const std::string serviceLocation = "",
                                   const int keepAliveInterval = 0)
    { return gimiCore.subscribers.addServiceType(majorTypeId, minorTypeId,
        serviceDescription, serviceLocation, keepAliveInterval); };

    /**
     * Remove a servicetype. If minor type undefined, removes all with given
     * major type.
     *
     * \param[in] majorTypeId
     * \param[in] minorTypeId
     * \return False if service-type was not found, else true.
     */
    inline bool removeProvidedService(const int majorTypeId,
                                      const int minorTypeId = GIMI_IGNOREMINORTYPE)
    { return gimiCore.subscribers.removeServiceType(majorTypeId, minorTypeId); };
    
    //// Service/type filtering ///////////////////////////////////////////////
    
    /**
     * Add a servicetype this client accepts.
     * Description and location information for this can also be added.
     * 
     * \param[in] majorTypeId Major-type of the service.
     * \param[in] minorTypeId Minor-type of the service.
     * \param[in] serviceDescription Optional description.
     * \param[in] serviceLocation Optional Location-information.
     * \return Boolean for success.
     */
    inline bool addAcceptedService(const int majorTypeId,
                                   const int minorTypeId = GIMI_IGNOREMINORTYPE,
                                   const std::string serviceDescription = "",
                                   const std::string serviceLocation = "")
    { return gimiCore.messageFilter.addWantedType(majorTypeId, minorTypeId,
        serviceDescription, serviceLocation); };
    /**
     * Remove a servicetype from accepted list. If minor type not defined,
     * remove major-type completely.
     *
     * \param[in] majorTypeId
     * \param[in] minorTypeId
     * \return False if given type was not found, else true.
     */
    inline bool removeAcceptedService(const int majorTypeId,
                                      const int minorTypeId = GIMI_IGNOREMINORTYPE)
    { return gimiCore.messageFilter.removeWantedType(majorTypeId, minorTypeId); };
    
    /**
     * Clears all accepted services.
     * 
     * \return Boolean for success.
     */
    inline bool clearAcceptedServices() { return gimiCore.messageFilter.clearWantedTypes(); };
    
    /**
     * Adds a messagetype we want to follow.
     * If minor-type not defined, follow all of major-type. This will remove
     * all previously added minor-types from the list.
     *
     * \deprecated This function is no longer in use, and is just a
     * wrapper for newer interface function
     * 'addAcceptedService()'. Use of this function for new
     * applications is deprecated.
     *
     * \param[in] majorTypeId \param[in] minorTypeId \return False if
     * given type already exists, else true.
     */
    inline bool addWantedTypeId(const int majorTypeId,
                                const int minorTypeId = GIMI_IGNOREMINORTYPE)
    { return addAcceptedService(majorTypeId, minorTypeId); }

    /**
     * Remove a messagetype from followed list. If minor type not defined,
     * remove major-type completely.
     *
     * \deprecated This function is no longer in use, and is just a
     * wrapper for newer interface function
     * 'removeAcceptedService()'. Use of this function for new
     * applications is deprecated.
     *
     * \param[in] majorTypeId
     * \param[in] minorTypeId
     * \return False if given type was not found, else true.
     */
    inline bool removeWantedTypeId(const int majorTypeId,
                                   const int minorTypeId = GIMI_IGNOREMINORTYPE)
    { return removeAcceptedService(majorTypeId, minorTypeId); }

    /**
     * Removes all typeid:s from the list.
     * 
     * \return True
     */
    inline bool clearWantedTypeIdList() { return clearAcceptedServices(); }
    

    /**
     * Checks whether the given Major/Minor pair is NOT used in
     * either table: Accepted or Provided services.
     *
     * \return                  'true' if the typeID pair is 'free' (it is
     *                          not registered as accepted or provided service).
     *                          'false' if the typeID pair is registered
     *                          to either one.
     */
    inline bool isFreeTypeId(const int majorTypeId,
                             const int minorTypeId) {
      return 
        !gimiCore.messageFilter.isRegisteredType(majorTypeId, minorTypeId) &&
        !gimiCore.subscribers.isRegisteredType(majorTypeId, minorTypeId);
    }
    //// Sender filtering /////////////////////////////////////////////////////
    
    /**
     * Adds a sender we want to follow.
     *
     * \param[in] sId SenderId of the sender we want to follow.
     * \return False if sender already existed, else true.
     */
    inline bool addWantedSenderId(const int sId)
    { return gimiCore.messageFilter.addWantedSender(sId); };

    /**
     * Remove a sender from followed list.
     *
     * \param[in] sId SenderId of the sender to remove.
     * \return False if sender was not found, else true.
     */
    inline bool removeWantedSenderId(const int sId)
    { return gimiCore.messageFilter.removeWantedSender(sId); };

    /**
     * Clears wanted senders list.
     * \return True
     */
    inline bool clearWantedSenders()
    { return gimiCore.messageFilter.clearWantedSenders(); };
    
    //// Redirections /////////////////////////////////////////////////////////
    
    /**
     * Reserves a messagebuffer id for use with redirections.
     * 
     * You should release the id when done.
     * 
     * It is not mandatory to use this reserving-system, but it is recommended
     * to avoid conflicts with redirections.
     * 
     * \return 0 if error occurred, else a new buffer identifier.
     */
    inline unsigned int reserveBufferIdentifier()
    { return gimiCore.messageBuffers.reserveBuffer(); };
    
    /**
     * Releases a reserved buffer identifier.
     * 
     * \param[in] bufferIdentifier Id to release.
     * \return True if buffer released successfully, false if id was not
     * reserved.
     */
    inline bool releaseBufferIdentifier(const unsigned int bufferIdentifier)
    { return gimiCore.messageBuffers.releaseBuffer(bufferIdentifier); };
    
    /**
     * Adds a redirection for a specific majortype to a new messagebuffer with
     * given id. Optionally also minortype and sender can be used as a
     * redirection criteria.
     * Default messagebuffer uses id 0.
     * 
     * If you add a second redirection, data will be duplicated also to that
     * queue.
     * 
     * \param[in] bufferIdentifier Id-number for the new buffer.
     * \param[in] majorTypeId Majortype for the redirected/duplicated data.
     * \param[in] minorTypeId Minortype for the redirected/duplicated data.
     * \param[in] senderName Sender of the redirected/duplicated data.
     * \return False if redirection already exists.
     */
    bool addRedirection(const unsigned int bufferIdentifier,
                        const int majorTypeId,
                        const int minorTypeId = -1,
                        const std::string &senderName = "");
    
    /**
     * Removes a redirection. Will only remove a specific redirection,
     * for example if minortype & sender are not specified only pure
     * majortype redirections will be removed in the given majortype.
     * 
     * \param[in] bufferIdentifier Id-number of the buffer.
     * \param[in] majorTypeId Majortype of data.
     * \param[in] minorTypeId Minortype of data.
     * \param[in] senderName Sender of the data.
     * \return False if redirection did not exist.
     */
    bool removeRedirection(const unsigned int bufferIdentifier,
                           const int majorTypeId,
                           const int minorTypeId = -1,
                           const std::string &senderName = "");

    /**
     * Gives a list of redirections currently added to GIMI. Redirections are
     * normally listed recursively, but if exactTypes is true, handling will
     * stop at first undefined value (For example, exactTypes is true and
     * majortype and minortype are defined, listing will include queues from
     * that majortype and that minortype but not from any of the senders. If
     * exactTypes is true and majortype is not defined, no redirections will
     * be listed).
     *
     * Usage and options are a bit complex, but enable lot of listing
     * combinations.
     *
     * All redirections can be listed by giving only the target list as
     * parameter.
     *
     * Method does not clean the target list prior to adding new redirections
     * to it, so usually an empty list should be given.
     *
     * About results, if type or sender is -1, it means that the redirection
     * rule will match all of that type/sender. (Queues from majortype will
     * have minortype -1 and empty sender).
     *
     * \param[out] redirections List into which redirections will be added.
     * \param[in] exactTypes If Types should be used as exact demands for
     * redirections or as starting points for recursive listing.
     * \param[in] majorTypeId Majortype in redirection
     * \param[in] minorTypeId Minortype in redirection
     * \param[in] senderName Sender in redirection
     * \return Number of redirections found.
     */
    int listRedirections(gimi::t_redirectionList &redirections,
                         const bool exactTypes = false,
                         const int majorTypeId = -1,
                         const int minorTypeId = -1,
                         const std::string &senderName = "");
    
    //// Connecting ///////////////////////////////////////////////////////////
    
    /**
     * Connects to tcpHub. If all parameters are configured with
     * readSettings, this method can be called without parameters.
     * 
     * \param[in] hubAddress Address of the hub to connect to.
     * \param[in] hubPort Port of the hub.
     * \param[in] ownName Wanted name in hub.
     * \param[in] aTimeout_ms timeout value in milliseconds to wait for hub reply.
     * \return Boolean for success.
     */
    inline bool connectToHub(const std::string &hubAddress = "",
                             const int hubPort = -1,
                             const std::string &ownName = "",
                             const int aTimeout_ms = 5000)
    { return connectToHubEx(hubAddress, hubPort, 
                            ownName, aTimeout_ms) == GIMI_OK ? true : false; };
    
    /**
     * Connects to tcpHub. If all parameters are configured with
     * readSettings, this method can be called without parameters.
     * This method returns error code instead of boolean.
     * 
     * \param[in] hubAddress Address of the hub to connect to.
     * \param[in] hubPort Port of the hub.
     * \param[in] ownName Wanted name in hub.
     * \param[in] aTimeout_ms timeout value in milliseconds to wait for hub reply.
     * \return GIMI_OK for success, GIMI_INVALIDNAME if client name was
     * already taken or invalid or GIMI_ERROR for other errors.
     */
    inline int connectToHubEx(const std::string &hubAddress = "",
                              const int hubPort = -1,
                              const std::string &ownName = "",
                              const bool aForceNameRegistration = false,
                              const std::string &aSecretKey = "clientnone",
                              const int aTimeout_ms = 5000)
    { return gimiCore.connectToHub(hubAddress, hubPort, ownName, 
                                   aForceNameRegistration, aSecretKey, aTimeout_ms); };
    
    //// Sending and receiving ////////////////////////////////////////////////
    
    /**
     * Send given data to target.
     *
     * \param[in] targetName Name of the message receiver.
     * \param[in] data Data to send.
     * \param[in] datalen Length of data to send.
     * \param[in] majorTypeId Major-type of the sent message.
     * \param[in] minorTypeId Minor-type of the sent message.
     * \param[in] messageId MessageId of the message. 0 if not defined.
     * \param[in] messageFlags Flags for message. 0 if not defined.
     * \return GIMI_OK if message was sent successfully to the hub.
     * GIMI_NOCONNECTION if connection to hub has been lost.
     * GIMI_ERROR if target could not be found or some other error occurred.
     */
    int send(const std::string &targetName,
             const char *data,
             const int datalen,
             const int majorTypeId,
             const int minorTypeId,
             const int messageId = 0,
             const int messageFlags = 0);

    /**
     * Send given data to target.
     *
     * \param[in] targetId Id of the message receiver.
     * \param[in] data Data to send.
     * \param[in] datalen Length of data to send.
     * \param[in] majorTypeId Major-type of the sent message.
     * \param[in] minorTypeId Minor-type of the sent message.
     * \param[in] messageId MessageId of the message. 0 if not defined.
     * \param[in] messageFlags Flags for message. 0 if not defined.
     * \return GIMI_OK if message was sent successfully to the hub.
     * GIMI_NOCONNECTION if connection to hub has been lost.
     * GIMI_ERROR some error occurred.
     */
    inline int send(const int targetId,
                    const char *data,
                    const int datalen,
                    const int majorTypeId,
                    const int minorTypeId,
                    const int messageId = 0,
                    const int messageFlags = 0)
    { return gimiCore.send(targetId, data, datalen, majorTypeId, minorTypeId,
        messageId, messageFlags); };

    /**
     * Sends given data to subscribers in subscriberList.
     *
     * \param[in] data Data to send.
     * \param[in] datalen Length of data to send.
     * \param[in] majorTypeId Major-type of the sent message.
     * \param[in] minorTypeId Minor-type of the sent message.
     * \param[in] messageId MessageId of the message. 0 if not defined.
     * \param[in] messageFlags Flags for message. 0 if not defined.
     * \param[in] maxUnsynced Maximum number of unreplied messages that
     * subscriber can have before sending to it is stopped. Default is
     * -1, messages are sent without synchronization (fastest).
     * \param[in] timeout If unsynced is set, this is used to determine when
     * waiting for a reply can be stopped and reset. Timeout is in milliseconds
     * \return GIMI_OK if message was sent successfully to the hub.
     * GIMI_NOCONNECTION if connection to hub has been lost.
     * GIMI_NOSUBSCRIBERS if there were no subscribers.
     * GIMI_INVALIDTYPE if type did not exist.
     * GIMI_ERROR if some error occurred.
     */
    inline int sendToSubscribers(const char *data,
                                 const int datalen,
                                 const int majorTypeId,
                                 const int minorTypeId,
                                 const int messageId = 0,
                                 const int messageFlags = 0,
                                 const int maxUnsynced = -1,
                                 const int timeout = 500)
    { return gimiCore.sendToSubscribers(data, datalen, majorTypeId, minorTypeId,
        messageId, messageFlags, maxUnsynced, timeout); };
    
    /**
     * Get message from messagequeue. If queue is empty, wait until timeout.
     * Receive can be set to wait for specific major & minor type messages if
     * wanted. If left empty, will return first encountered message.
     *
     * \warning When receiving all types, buffers are checked by type from
     * lower to higher values, and first found message is returned. This can
     * lead to starvation so that only the first type is returned if client
     * calling receive is not fast enough.
     * 
     * \param[out] message Received message is moved to given messagestruct.
     * \param[in] timeout How long to wait for message (in ms). -1 waits until
     * a message arrives.
     * \param[in] majorTypeId Wait for a specific major-type.
     * \param[in] minorTypeId Wait for a specific minor-type.
     * \param[in] bufferId Receive from a different buffer than default (0)
     * \return GIMI_OK for a successfully received message.
     * GIMI_NOMSGS if no messages were found in buffer. (when timeout 0)
     * GIMI_TIMEOUT if no messages were in buffer or arrived during timeout
     * time.
     * GIMI_ERROR some error occurred.
     * GIMI_INTERRUPT if GIMI was stopped before or while waiting for a message.
     */
    int receive(gimi::GIMIMessage &message,
                const int timeout,
                const int majorTypeId = GIMI_IGNOREMAJORTYPE,
                const int minorTypeId = GIMI_IGNOREMINORTYPE,
                const unsigned int bufferId = 0);

    /**
     * Get message from queue by range, will return first encountered message
     * from given range.
     * If you want to receive a specific major- or minortype use same value for
     * min & max.
     * 
     * \param[out] message Received message is moved to given messagestruct.
     * \param[in] timeout ow long to wait for message (in ms). -1 waits until
     * a message arrives.
     * \param[in] majorTypeIdMin Lowest major-type to receive.
     * \param[in] majorTypeIdMax Highest major-type to receive.
     * \param[in] minorTypeIdMin Lowest minor-type to receive.
     * \param[in] minorTypeIdMax Highest minor-type to receive.
     * \param[in] bufferId Receive from a different buffer than default (0)
     * \return GIMI_OK for a successfully received message.
     * GIMI_NOMSGS if no messages were found in buffer. (when timout 0)
     * GIMI_TIMEOUT if no messages were in buffer or arrived during timeout
     * time.
     * GIMI_ERROR some error occurred.
     * GIMI_INTERRUPT if GIMI was stopped before or while waiting for a message.
     */
    int receiveRange(gimi::GIMIMessage &message,
                     const int timeout,
                     const int majorTypeIdMin,
                     const int majorTypeIdMax,
                     const int minorTypeIdMin = GIMI_IGNOREMINORTYPE,
                     const int minorTypeIdMax = GIMI_IGNOREMINORTYPE,
                     const unsigned int bufferId = 0);
    
    //// Service discovery ////////////////////////////////////////////////////
    
    /**
     * Sends servicediscovery to all clients registered to tcpHub-network.
     * \warning This method will take at least clienttimeout milliseconds to
     * run.
     * 
     * Queries are sent to all clients after which method waits clienttimeout
     * milliseconds before checking arrived replies.
     * 
     * Clients that replied but have no services are added to the list with a
     * dummy majorTypeId 0.
     * 
     * \param[out] services List of clients and their services.
     * \param[in] clienttimeout Timeout to wait for replies from clients.
     * \return GIMI_OK for success, GIMI_ERROR connection etc. errors.
     */
    inline int serviceDiscovery(gimi::t_serviceList &services,
                                const int clienttimeout = 5000)
    { 
      return gimiCore.serviceDiscovery(services, clienttimeout);
    }; 
    
    /**
     * Sends servicediscovery to target and waits answer for at most timeoutms
     * before returning.
     * 
     * Clients that replied but have no services are added to the list with a
     * dummy majorTypeId 0.
     * 
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     *
     * \param[out] servicelist List of services is written here. If list
     * already has content, it will be erased.
     * \param[in] clientname Name of target client.
     * \param[in] timeoutms Timeout in milliseconds.
     * \return GIMI_OK for success, GIMI_TIMEOUT for timeout, GIMI_INTERRUPT
     * when GIMI was stopped while waiting a reply, GIMI_ERROR for other errors.
     */
    int serviceDiscoveryToTarget(gimi::t_serviceList &servicelist,
                                 const std::string &clientname,
                                 const int timeoutms = 5000);

    /**
     * Sends servicediscovery to target and waits answer for at most timeoutms
     * before returning.
     *
     * Clients that replied but have no services are added to the list with a
     * dummy majorTypeId 0.
     * 
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     * 
     * \param[out] servicelist List of services is written here. If list
     * already has content, it will be erased.
     * \param[in] clientId Id of target client.
     * \param[in] timeoutms Timeout in milliseconds.
     * \return GIMI_OK for success, GIMI_TIMEOUT for timeout, GIMI_INTERRUPT
     * when GIMI was stopped while waiting a reply, GIMI_ERROR for other errors.
     */
    int serviceDiscoveryToTarget(gimi::t_serviceList &servicelist,
                                 const int clientId,
                                 const int timeoutms = 5000);
    
    //// Subscribing //////////////////////////////////////////////////////////

    /**
     * Subscribes to another client's service.
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     * 
     * \param[in] clientName From whom to subscribe data (as a name).
     * \param[in] majorServiceId Type of the service we want to subscribe.
     * \param[in] minorServiceId Instance of the service (usually)
     * \param[in] timeoutms Time to wait for reply.
     * \param[in] updateInterval Timeout for automatically resubscribing
     * service if no data is received (milliseconds). When timeout is < 1,
     * it is not used.
     *
     * \note The GIMI_INVALIDCLIENT return value is OK in most cases -
     * if the subscription was already present there was no need to
     * redo it. So, applications should generally accept both: OK and
     * INVALIDCLIENT as correct results from this function.
     *
     * \return GIMI_OK if subscription succeeded,
     * GIMI_INVALIDCLIENT if this client had already subscribed to service,
     * GIMI_INVALIDTYPE if target was reached but it did not provide specified
     * service,
     * GIMI_TIMEOUT if no reply arrived within timeout milliseconds,
     * GIMI_NOCONNECTION if connection to hub has been lost,
     * GIMI_ERROR if some other error occurred.
     */
    int subscribeToService(const std::string &clientName,
                           const int majorServiceId,
                           const int minorServiceId,
                           const int timeoutms = 5000,
                           const int updateInterval = -1);

    /**
     * Subscribes to another client's service.
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     * 
     * \param[in] clientId From whom to subscribe data (as hubid).
     * \param[in] majorServiceId Type of the service we want to subscribe.
     * \param[in] minorServiceId Instance of the service (usually)
     * \param[in] timeoutms Time to wait for reply.
     * \param[in] updateInterval Timeout for automatically resubscribing
     * service if no data is received (milliseconds). When timeout is < 1,
     * it is not used.
     *
     * \note The GIMI_INVALIDCLIENT return value is OK in most cases -
     * if the subscription was already present there was no need to
     * redo it. So, applications should generally accept both: OK and
     * INVALIDCLIENT as correct results from this function.
     *
     * \return GIMI_OK if subscription succeeded,
     * GIMI_INVALIDCLIENT if this client had already subscribed to service,
     * GIMI_INVALIDTYPE if target was reached but it did not provide specified
     * service,
     * GIMI_TIMEOUT if no reply arrived within timeout milliseconds,
     * GIMI_NOCONNECTION if connection to hub has been lost,
     * GIMI_ERROR if some other error occurred.
     */
    int subscribeToService(const int clientId,
                           const int majorServiceId,
                           const int minorServiceId,
                           const int timeoutms = 5000,
                           const int updateInterval = -1);

    /**
     * Unsubscribes from a subscribed service.
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     * 
     * \param[in] clientName Target (name)
     * \param[in] majorServiceId Type of the service we want to unsubscribe.
     * \param[in] minorServiceId Instance of the service (usually)
     * \param[in] timeoutms Time to wait for reply.
     * \return GIMI_OK if unsubscribe succeeded,
     * GIMI_INVALIDCLIENT if this client was not subscribed to service,
     * GIMI_INVALIDTYPE if target was reached but we were not subscribed to
     * specified service,
     * GIMI_TIMEOUT if no reply arrived within timeout milliseconds,
     * GIMI_NOCONNECTION if connection to hub has been lost,
     * GIMI_ERROR if some other error occurred.
     */
    int unsubscribeService(const std::string &clientName,
                           const int majorServiceId,
                           const int minorServiceId,
                           const int timeoutms = 5000);

    /**
     * Unsubscribes from a subscribed service.
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     * 
     * \param[in] clientId Target (id)
     * \param[in] majorServiceId Type of the service we want to unsubscribe.
     * \param[in] minorServiceId Instance of the service (usually)
     * \param[in] timeoutms Time to wait for reply.
     * \return GIMI_OK if unsubscribe succeeded,
     * GIMI_INVALIDCLIENT if this client was not subscribed to service,
     * GIMI_INVALIDTYPE if target was reached but we were not subscribed to
     * specified service,
     * GIMI_TIMEOUT if no reply arrived within timeout milliseconds,
     * GIMI_NOCONNECTION if connection to hub has been lost,
     * GIMI_ERROR if some other error occurred.
     */
    int unsubscribeService(const int clientId,
                           const int majorServiceId,
                           const int minorServiceId,
                           const int timeoutms = 5000);
    
    //// Misc. actions ////////////////////////////////////////////////////////
    
    /**
     * Ping target client with timeout in milliseconds.
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     *
     * \param[in] clientname Name of the client
     * \param[in] timeoutms How long to wait for reply
     * \return Ping roundtrip time when reply received (0 or greater), else
     * GIMI_TIMEOUT if reply did not arrive within timeout,
     * GIMI_INTERRUPT if GIMI was stopped while waiting for a reply,
     * GIMI_ERROR for invalid clientname etc.
     */
    int ping(const std::string &clientname,
             const int timeoutms = 5000);

    /**
     * Ping target client with timeout in milliseconds.
     * \warning This method may not return until timeout has passed, so adjust
     * your timeout accordingly.
     * 
     * \param[in] clientId Hubid of the client
     * \param[in] timeoutms How long to wait for reply
     * \return Ping roundtrip time when reply received (0 or greater), else
     * GIMI_TIMEOUT if reply did not arrive within timeout,
     * GIMI_INTERRUPT if GIMI was stopped while waiting for a reply,
     * GIMI_ERROR for invalid clientname etc.
     */
    inline int ping(const int clientId,
                    const int timeoutms = 5000)
    { return gimiCore.ping(clientId, timeoutms); };
    
    /**
     * Set debuglevel on target client.
     * 
     * \param[in] clientname Target client (name)
     * \param[in] debugLevel New debuglevel for target.
     * \return GIMI_OK if command was successfully sent,
     * GIMI_NOCONNECTION if we were not connected,
     * GIMI_ERROR for other errors.
     */
    int setTargetDebugLevel(const std::string &clientname,
                            const int debugLevel);
    
    /**
     * Set debuglevel on target client.
     * 
     * \param[in] clientId Target client (hubid)
     * \param[in] debugLevel New debuglevel for target.
     * \return GIMI_OK if command was successfully sent,
     * GIMI_NOCONNECTION if we were not connected,
     * GIMI_ERROR for other errors.
     */
    inline int setTargetDebugLevel(const int clientId,
                                   const int debugLevel)
    { return gimiCore.setTargetDebugLevel(clientId, debugLevel); };

    /**
     * Query client name by id.
     * If no name is found for given id, clientname.empty() == true.
     * 
     * \param[out] clientname String in which to write received client name.
     * \param[in] idno Client's id number in tcpHub.
     * \param[in] aTimeout_ms timeout value in milliseconds to wait for hub reply.
     * \return False if could not connect, else true.
     */
    bool findClientName(std::string &clientname,
                        const int idno,
                        const unsigned int aTimeout_ms = 120000);

    /**
     * Query client id from nif by name.
     * Returns < 1 if something went wrong, else clientid.
     *
     * \param[in] clientname Name of client to query.
     * \param[in] aTimeout_ms timeout value in milliseconds to wait for hub reply.
     * \return GIMI_NOCONNECTION if not connected, 0 if client was not found,
     * else clientid.
     */
    int findClientId(const std::string &clientname,
                     const unsigned int aTimeout_ms = 120000);

    //// GIMI-instance information ////////////////////////////////////////////
    
    /**
     * Returns number of subscribers to a service. If minor-type is not
     * defined, returns number of subscribers in all minor-types of defined
     * major-type.
     *
     * \param[in] majorTypeId Id of the servicetype from which to get count.
     * \param[in] minorTypeId Id of the servicetype from which to get count.
     * \return GIMI_INVALIDTYPE if service-type not found, else number of subscribers.
     */
    int getSubscriberCount(const int majorTypeId,
                           const int minorTypeId = GIMI_IGNOREMINORTYPE) const;

    /**
     * Returns current clientid to hub.
     * \return 0 if not connected, else clientid
     */
    inline int getOwnId() const { return gimiCore.iConnector->getHubId(); }
    
    /**
     * Sets client-description.
     */
    inline void setDescription(const std::string cDescription)
    { gimiCore.setDescription(cDescription); }
    
    /**
     * Returns current client-description.
     */
    inline std::string getDescription() const { return gimiCore.getDescription(); }

    /**
     * Returns address of tcpHub this GIMI is connected to.
     * \return Empty string if connectToHub has not been called, else address
     * to currently connected tcpHub.
     */
    inline std::string getHubAddress() const
    { return gimiCore.iConnector->getHubAddress(); }

    /**
     * Returns port of tcpHub this GIMI is connected to.
     * \return -1 if connectToHub has not been called, else port of currently
     * connected tcpHub.
     */
    inline int getHubPort() const { return gimiCore.iConnector->getHubPort(); }
    
    /**
     * Return if GIMI has connection to hub.
     * 
     * \return Boolean for connection validity.
     */
    inline bool isConnected() const { return gimiCore.iConnector->isConnected(); }

    ///////////////////////////////////////////////////////////////////////////
    
    /**
     * Clears all messages from messagebuffer.
     *
     * \return Boolean for success.
     */
    inline bool clearMessageBuffer(const unsigned int bufferId = 0)
    { gimiCore.messageBuffers.clearBuffer(bufferId); return true; };

    /**
     * Stops listener thread and all methods with timeouts in GIMI
     * (receive, serviceDiscovery, ping).
     * 
     * \warning Reusing the class after stop is not recommended, this is mainly
     * meant to be used when stopping all actions due to signal etc.
     * 
     */
    inline void stop() { gimiCore.stop(); };

  private:
    // Prevents use of copy constructors
    GIMI(const GIMI& GIMI);
    const GIMI& operator= (const GIMI& right);
    
    GIMICore gimiCore;
  };

} // end namespace gimi
